﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Mail;
using System.Net;
using System.Configuration;
using ExtraClub.Domain.Entities;
using SubSonic;
using System.Text;
using System.Diagnostics;
using System.Threading;

/// <summary>
/// Summary description for job
/// </summary>
public static class Job {
    public class ThreadContext {
        public EventWaitHandle Autoevent = new AutoResetEvent(false);
        public SmtpClient client = null;
        public VwMailJob item = null;
    }
    private const double JOBMAIL_HOLDON = 15; // minutes
    private static int SmtpServerConnectionLimit = default(int);
    private static List<AutoResetEvent> waitEvents = new List<AutoResetEvent>();

    internal static void SendBatchMail() {
        SmtpServerConnectionLimit = int.Parse(ConfigurationManager.AppSettings.Get("SmtpServerConnectionLimit"));

        VwMailJobCollection jobs = Select.AllColumnsFrom<VwMailJob>()
           .WhereExpression(VwMailJob.Columns.Sent).IsNull()
               .And(VwMailJob.Columns.Suspended).IsEqualTo(false)
               .And(VwMailJob.Columns.Created).IsLessThan(DateTime.Now.AddMinutes(-JOBMAIL_HOLDON))
               .AndExpression(VwMailJob.Columns.AttemptNumber).IsLessThan(SmtpServerConnectionLimit)
               .Or(VwMailJob.Columns.AttemptNumber).IsNull()
               .ExecuteAsCollection<VwMailJobCollection>();

        jobs.GetList().ForEach(item =>
        {
            AutoResetEvent autoevent = new AutoResetEvent(false);
            waitEvents.Add(autoevent);

            ThreadContext context = new ThreadContext();
            context.Autoevent = autoevent;

            context.client = new SmtpClient();
            context.client.EnableSsl = true;
            context.client.Timeout = TimeSpan.FromSeconds(60).Milliseconds;
            context.item = item;

            ThreadPool.QueueUserWorkItem(delegate(object obj) {
                context = (ThreadContext)obj;

                Send(context);

                context.Autoevent.Set();
            }, context);
        });
        if (waitEvents.Count > 0) {
            bool wait = WaitHandle.WaitAll(waitEvents.ToArray(), TimeSpan.FromMinutes(30), true);
            if (!wait) {
                Debug.WriteLine("Timeout occurred.");
            }
        }
    }
    private static void Send(ThreadContext context) {
        SmtpClient client = context.client;
        VwMailJob item = context.item;
        try {
            MailMessage message = new MailMessage();
            message.From = new MailAddress(item.EmailFrom);
            message.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
            //message.Sender = message.From;
            message.To.Add(new MailAddress(item.EmailTo));

            message.Subject = item.Subject;
            message.SubjectEncoding = Encoding.UTF8;

            message.Headers.Add("X-SME-EmailID", item.Id.ToString());
            message.Headers.Add("X-SME-Attempts", ((item.AttemptNumber ?? 0) + 1).ToString());
            message.Headers.Add("X-SME-AppDomain", AppDomain.CurrentDomain.FriendlyName);
            message.Headers.Add("X-SME-Sender", item.EmailFrom);

            message.IsBodyHtml = true;
            message.Body = item.Message;
            message.BodyEncoding = Encoding.UTF8;

            client.Send(message);
            message.Dispose();

            EmailDeQueue(item.Id);

        } catch (Exception e) {
            SetFailedAtempt(item.Id);
            Debug.WriteLine(e.Message + " : " + (e.InnerException != null ? e.InnerException.Message : String.Empty));
        }
    }
    private static void EmailDeQueue(int id) {
        MailJob job = MailJob.FetchByID(id);
        try {
            job.Sent = DateTime.Now;
            job.Save();
        } catch (Exception e) {
            Debug.WriteLine(e.Message + " : " + (e.InnerException != null ? e.InnerException.Message : String.Empty));
        }
    }

    private static void SetFailedAtempt(int id) {
        MailJob job = MailJob.FetchByID(id);
        try {
            int atempt = job.AttemptNumber ?? 0;
            job.AttemptNumber = ++atempt;
            job.LastAttemptDate = DateTime.Now;
            job.Save();
        } catch (Exception e) {
            Debug.WriteLine(e.Message + " : " + (e.InnerException != null ? e.InnerException.Message : String.Empty));
        }
    }
}
